﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Pipes.Queries.Model.Query
{
  public class Operation : IExpression
  {
    private IList<IExpression> _args;
    private Operator _type;

    public IList<IExpression> Args
    {
      get
      {
        return _args;
      }
    }
    public bool Deterministic { get { return true; } }
    public Pipes.Queries.MetaData.GeneralDataType InferredType { get; set; }
    public Operator Type 
    {
      get
      {
        return _type;
      }
      set
      {
        _type = value;
        switch (_type)
        {
          case Operator.Addition:
          case Operator.BitwiseAnd:
          case Operator.BitwiseOr:
          case Operator.BitwiseXor:
          case Operator.Division:
          case Operator.IntegerDivision:
          case Operator.Modulus:
          case Operator.Multiplication:
          case Operator.Negative:
          case Operator.Power:
          case Operator.Subtraction:
            this.InferredType = Pipes.Queries.MetaData.GeneralDataType.numeric;
            break;
          case Operator.Concatenation:
            this.InferredType = Pipes.Queries.MetaData.GeneralDataType.@string;
            break;
          case Operator.Equals:
          case Operator.Equals_NullSafe:
          case Operator.GreaterEqual:
          case Operator.GreaterThan:
          case Operator.In:
          case Operator.Is:
          case Operator.LessEqual:
          case Operator.LessThan:
          case Operator.PatternMatch:
          case Operator.LogicalAnd:
          case Operator.LogicalOr:
          case Operator.NotEqual:
            this.InferredType = Pipes.Queries.MetaData.GeneralDataType.boolean;
            break;
          default:
            this.InferredType = Pipes.Queries.MetaData.GeneralDataType.unknown;
            break;
        }
      }
    }

    public Operation()
    {
      _args = new List<IExpression>();
    }
    public Operation(IEnumerable<IExpression> args)
    {
      _args = new List<IExpression>(args);
    }

    public object Evaluate()
    {
      return Execution.OperatorExecution.Execute(this.Type, this.Args);
    }

    public override string ToString()
    {
      var builder = new StringBuilder();
      for (var i = 0; i < this.Args.Count; i++)
      {
        if (i > 0)
        {
          builder.Append(" ");
          builder.Append(RenderOperator(this.Type));
          builder.Append(" ");
        }
        builder.Append(this.Args[i].ToString());
      }

      return builder.ToString();
    }

    public static string RenderOperator(Operator type)
    {
      switch (type)
      {
        case Operator.Ambig_BitwiseAndConcat:
          return "&";
        case Operator.Ambig_AdditionConcat:
          return "+";
        case Operator.Ambig_LogicalOrConcat:
          return "||";
        case Operator.Addition:
          return "+";
        case Operator.BitwiseAnd:
          return "&";
        case Operator.BitwiseOr:
          return "|";
        case Operator.BitwiseXor:
          return "^";
        case Operator.Concatenation:
          return "||";
        case Operator.Division:
          return "/";
        case Operator.Equals:
          return "=";
        case Operator.Equals_NullSafe:
          return "<=>";
        case Operator.GreaterEqual:
          return ">=";
        case Operator.GreaterThan:
          return ">";
        case Operator.In:
          return "IN";
        case Operator.IntegerDivision:
          return "IDIV";
        case Operator.Is:
          return "IS";
        case Operator.LessEqual:
          return "<=";
        case Operator.LessThan:
          return "<";
        case Operator.PatternMatch:
          return "LIKE";
        case Operator.LogicalAnd:
          return "AND";
        case Operator.LogicalOr:
          return "OR";
        case Operator.Modulus:
          return "%";
        case Operator.Multiplication:
          return "*";
        case Operator.NotEqual:
          return "<>";
        case Operator.Power:
          return "**";
        case Operator.Negative:
        case Operator.Subtraction:
          return "-";
        case Operator.BitwiseNot:
          return "^";
        default:
          return string.Empty;
      }
    }

    public void Visit(Parser.IQueryVisitor visitor)
    {
      visitor.Visit(this);
    }
  }
}
